package com.xbongbong.saas.analytical.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.help.team.TeamUserHelp;
import com.xbongbong.paas.pojo.dto.ValidateDataDTO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.DateTimeUtil;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.enums.ErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.OutstockErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SettleAccountErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.WarehouseErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.saas.analytical.SaasValidateAnalyticalService;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.domain.entity.ProductStockEntity;
import com.xbongbong.saas.domain.entity.ProductWarehouseEntity;
import com.xbongbong.saas.domain.entity.SettleAccountsEntity;
import com.xbongbong.saas.domain.entity.ext.ProductEntityExt;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.CostAdjustEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.product.CostAdjustProductEnum;
import com.xbongbong.saas.help.BatchKeyHelp;
import com.xbongbong.saas.help.SaveFormatHelp;
import com.xbongbong.saas.model.ProductModel;
import com.xbongbong.saas.model.ProductStockModel;
import com.xbongbong.saas.model.ProductWarehouseModel;
import com.xbongbong.saas.model.SettleAccountsModel;
import com.xbongbong.util.ValidateUtil;
import org.apache.commons.collections4.MapUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author 吴洪波
 * @date 2021/12/05 16:46
 */
@Service("costAdjustValidateAnalyticalServiceImpl")
public class CostAdjustValidateAnalyticalServiceImpl implements SaasValidateAnalyticalService {
    @Resource
    private ProductStockModel productStockModel;

    @Resource
    private ProductWarehouseModel productWarehouseModel;
    @Resource
    private ProductModel productModel;
    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private SettleAccountsModel settleAccountsModel;
    @Resource
    private TeamUserHelp teamUserHelp;

    @Override
    public List<Integer> type() {
        return Arrays.asList(XbbRefTypeEnum.COST_ADJUST.getCode());
    }

    @Override
    public void analyticalData(ValidateDataDTO validateDataDTO) throws XbbException {
        beforeSave(validateDataDTO);
        //没有负责团队的 单负责人
        teamUserHelp.beforeVerifyUserTeam(validateDataDTO);
    }
    
    public void beforeSave(ValidateDataDTO validateDataDTO) throws XbbException {
        // 表单数据
        JSONObject data = validateDataDTO.getData();
        // 公司ID
        String corpid = validateDataDTO.getCorpid();
        // 校验是否存在调整单日期不能小于最近一次结账日期，并且是否在结账中
        this.settleAccountsCheck(data.getLong(CostAdjustEnum.ADJUST_TIME.getAttr()), corpid);
        // 保存时批次号必填校验, 只有新建的时候才校验
        if (Objects.isNull(validateDataDTO.getDataId())) {
            SaveFormatHelp.batchSaveRequiredCheck(validateDataDTO);
        }
        // 获取调整单的产品信息
        JSONArray productJsonArray = data.getJSONArray(CostAdjustEnum.PRODUCT.getAttr());
        String productAttr = CostAdjustProductEnum.PRODUCT.getAttr();
        // 将产品ID取出
        List<Long> productIdList = new ArrayList<>();
        for (int i = 0; i < productJsonArray.size(); i++) {
            JSONObject productJson = productJsonArray.getJSONObject(i);
            if (Objects.isNull(productJson.get(CostAdjustProductEnum.ADJUST_AMOUNT.getAttr()))) {
                throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212052);
            }
            Long productId = productJson.getLong(productAttr);
            productIdList.add(productId);
        }
        Object warehouseObject = data.get(CostAdjustEnum.WAREHOUSE_ID.getAttr());
        if (Objects.nonNull(warehouseObject)) {
            if (warehouseObject instanceof Collection) {
                JSONArray warehouseList = data.getJSONArray(CostAdjustEnum.WAREHOUSE_ID.getAttr());
                if (CollectionsUtil.isNotEmpty(warehouseList)) {
                    JSONObject warehouseInfo = warehouseList.getJSONObject(0);
                    if (MapUtils.isEmpty(warehouseInfo)) {
                        throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.WAREHOUSE_INFO_NOT_EMPTY);
                    }
                    String warehouseName = warehouseInfo.getString(BusinessConstant.NAME);
                    Long warehouseId = warehouseInfo.getLong(BusinessConstant.ID);
                    data.put(CostAdjustEnum.WAREHOUSE_ID.getAttr(), warehouseId);
                    data.put(CostAdjustEnum.WAREHOUSE_ID_LINKED_TEXT.getAttr(), warehouseName);
                }
            } else {
                Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                params.put(ParameterConstant.CORPID,corpid);
                params.put(ParameterConstant.DEL,DelEnum.NORMAL.getDel());
                params.put("warehouseName", data.get(CostAdjustEnum.WAREHOUSE_ID.getAttr()));
                List<ProductWarehouseEntity> warehouseEntityList = productWarehouseModel.findEntitysForUpdate(params);
                if (CollectionsUtil.isEmpty(warehouseEntityList)) {
                    throw new XbbException(WarehouseErrorCodeEnum.API_ERROR_244005);
                }
                ProductWarehouseEntity productWarehouseEntity = warehouseEntityList.get(0);
                data.put(CostAdjustEnum.WAREHOUSE_ID.getAttr(), productWarehouseEntity.getWarehouseId());
                data.put(CostAdjustEnum.WAREHOUSE_ID_LINKED_TEXT.getAttr(), productWarehouseEntity.getWarehouseName());
            }
        }
        // 查询入库的产品信息
        List<ProductEntityExt> productExtList = productModel.getProductListByIdIn(corpid, productIdList, DelEnum.NORMAL.getDel());
        // 产品ID对应的产品信息 key->产品ID，value->产品信息
        Map<Long, ProductEntityExt> productExtMap = productExtList.stream().collect(Collectors.toMap(ProductEntityExt::getId, item -> item));
        // 是否启用批次的Attr
        String enableBatchShelfLifeAttr = ProductEnum.ENABLE_BATCH_SHELF_LIFE.getAttr();
        // 是否启用批次的SaasAttr
        String enableBatchShelfLifeSaasAttr = ProductEnum.ENABLE_BATCH_SHELF_LIFE.getSaasAttr();
        // 是否是多单位
        String enableMultiUnitAttr = ProductEnum.ENABLE_MULTI_UNIT.getAttr();
        String enableMultiUnitSaasAttr = ProductEnum.ENABLE_MULTI_UNIT.getSaasAttr();
        // 单位
        String unitAttr = ProductEnum.UNIT.getAttr();
        // 产品ID对应的名字
        Map<Long, String> productNameMap = new HashMap<>();
        for (int i = 0; i < productJsonArray.size(); i++) {
            JSONObject productJson = productJsonArray.getJSONObject(i);
            // 调整单的产品ID
            Long productId = productJson.getLong(productAttr);
            // 根据ID获取产品信息
            ProductEntityExt productEntityExt = productExtMap.get(productId);
            if (Objects.isNull(productEntityExt)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100018);
            }
            JSONObject productData = productEntityExt.getData();
            productNameMap.put(productId, productData.getString(ProductEnum.NAME.getAttr()));
            // 跟新否启用批次字段
            productJson.put(enableMultiUnitAttr, productData.getInteger(enableMultiUnitAttr));
            productJson.put(enableMultiUnitSaasAttr, productData.getInteger(enableMultiUnitAttr));
            productJson.put(CostAdjustProductEnum.BASIC_UNIT.getAttr(), productData.get(unitAttr));
            productJson.put(enableBatchShelfLifeSaasAttr, productData.getInteger(enableBatchShelfLifeAttr));
            productJson.put(enableBatchShelfLifeAttr, productData.getInteger(enableBatchShelfLifeAttr));
            productJson.put(ProductEnum.PARENT_ID.getSaasAttr(), productData.get(ProductEnum.PARENT_ID.getAttr()));
            productJson.put(ProductEnum.PARENT_ID.getAttr(), productData.get(ProductEnum.PARENT_ID.getAttr()));
        }
        // 产品校验: 产品总仓、分仓、批次的库存不为0校验和唯一性校验
        this.productCheck(productJsonArray, corpid, productNameMap);
        // 校验编号长度不超过100且编号存在
        Map<String, FieldAttrEntity> explainMap = validateDataDTO.getExplainMap();
        FieldAttrEntity fieldAttrEntity = new FieldAttrEntity();
        FieldAttrEntity sheetNo = explainMap.getOrDefault(CostAdjustEnum.ADJUST_NO.getAttr(), fieldAttrEntity);
        if (Objects.nonNull(sheetNo) && Objects.equals(sheetNo.getStrictController(), BasicConstant.ZERO)) {
            ValidateUtil.checkSerialNumber(validateDataDTO.getSerialNo(), Objects.toString(sheetNo.getAttrName(), ""));
        }
    }

    /**
     * 结账校验
     *
     * @param adjustTime 调整单日期
     * @param corpid     公司ID
     * @throws XbbException 业务异常
     */
    private void settleAccountsCheck(Long adjustTime, String corpid) throws XbbException {
        // 是否在结账中
        Boolean isSettleAccounts = paasRedisHelper.hasKey(RedisPrefixConstant.SETTLE_ACCOUNTS, corpid);
        if (isSettleAccounts) {
            throw new XbbException(SettleAccountErrorCodeEnum.API_ERROR_254007);
        }
        // 获取最近一次结账记录
        SettleAccountsEntity accountsEntity = settleAccountsModel.getLastSettleAccounts(corpid);
        if (Objects.nonNull(accountsEntity) && Objects.nonNull(accountsEntity.getCheckOutTime())) {
            // 调整单日期不能小于最近一次结账日期
            if (Objects.isNull(adjustTime) || adjustTime < DateTimeUtil.getTodayEndInt(accountsEntity.getCheckOutTime())) {
                throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212051);
            }
        }
    }

    /**
     * 产品总仓、分仓、批次的库存不为0校验和唯一性校验
     *
     * @param productJsonArray 调整产品信息
     * @param corpid           公司ID
     * @param productNameMap   产品名字信息
     * @throws XbbException 业务异常
     */
    private void productCheck(JSONArray productJsonArray, String corpid, Map<Long, String> productNameMap) throws XbbException {
        String productAttr = CostAdjustProductEnum.PRODUCT.getAttr();
        Set<String> batchKeySet = new HashSet<>();
        Set<String> warehouseKeyList = new HashSet<>();
        Set<String> onlyProductList = new HashSet<>();
        for (int i = 0; i < productJsonArray.size(); i++) {
            JSONObject productJson = productJsonArray.getJSONObject(i);
            // 产品ID
            Long productId = productJson.getLong(productAttr);
            // 仓库ID
            Long warehouseId = this.getWarehouseId(productJson.getJSONArray(CostAdjustProductEnum.WAREHOUSE.getAttr()));
            Integer enableBatchShelfLife = productJson.getInteger(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getSaasAttr());
            String onlyProduct;
            if (Objects.equals(enableBatchShelfLife, 1)) {
                String batchKey = BatchKeyHelp.getBatchKey(productId, warehouseId, productJson.getString(CostAdjustProductEnum.BATCH.getAttr()),
                        productJson.getLong(CostAdjustProductEnum.PRODUCE_DATE.getAttr()), getGuaranteePeriod(productJson.getLong(CostAdjustProductEnum.GUARANTEE_PERIOD.getAttr())));
                onlyProduct = batchKey;
                batchKeySet.add(batchKey);
            } else {
                onlyProduct = productId +"_"+ warehouseId;
                warehouseKeyList.add(onlyProduct);

            }
            if (onlyProductList.contains(onlyProduct)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100018, MessageConstant.EXIST_DUPLICATE_PRODUCT);
            }
            onlyProductList.add(onlyProduct);
        }
        if (CollectionsUtil.isNotEmpty(batchKeySet)) {
            Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            params.put("del", DelEnum.NORMAL.getDel());
            params.put(ParameterConstant.CORPID, corpid);
            params.put("batchKeyIn", batchKeySet);
            List<ProductStockEntity> productStockEntityList = productStockModel.findEntitys(params);
            if (CollectionsUtil.isEmpty(productStockEntityList)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.WAREHOUSE_NOT_EXIST);
            }
            Map<String, ProductStockEntity> stockEntityMap = productStockEntityList.stream().collect(Collectors.toMap(ProductStockEntity::getBatchKey, entity -> entity));
            for (String batchKey : batchKeySet) {
                if (!stockEntityMap.containsKey(batchKey)) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.NOT_EXIST_BATCH);
                }
            }
            // 校验库存是否为0
            for (ProductStockEntity productStockEntity : productStockEntityList) {
                if (Objects.isNull(productStockEntity.getNum()) || Objects.equals(productStockEntity.getNum().compareTo(0D), 0)) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.PRODUCTS_STOCK_IS_ZERO, productNameMap.get(productStockEntity.getProductId()));
                }
            }
        }
        // 查询产品仓库信息
        if (CollectionsUtil.isNotEmpty(warehouseKeyList)) {
            Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            params.put("del", DelEnum.NORMAL.getDel());
            params.put(ParameterConstant.CORPID, corpid);
            params.put("warehouseKeyIn", warehouseKeyList);
            List<ProductWarehouseEntity> productWarehouseEntities = productWarehouseModel.findEntitysWithWarehouseChecked(params, null);
            if (CollectionsUtil.isEmpty(productWarehouseEntities)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.WAREHOUSE_NOT_EXIST);
            }
            Map<String, ProductWarehouseEntity> warehouseEntityMap = productWarehouseEntities.stream().collect(Collectors.toMap(ProductWarehouseEntity::getWarehouseKey, entity -> entity));
            for (String warehouseKey : warehouseKeyList) {
                if (!warehouseEntityMap.containsKey(warehouseKey)) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.WAREHOUSE_NOT_EXIST);
                }
            }
            for (ProductWarehouseEntity warehouseEntity : productWarehouseEntities) {
                if (Objects.isNull(warehouseEntity.getNum()) || Objects.equals(warehouseEntity.getNum().compareTo(0D), 0)) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.PRODUCTS_STOCK_IS_ZERO, productNameMap.get(warehouseEntity.getProductId()));
                }
            }
        }
    }


    /**
     * 获取仓库ID
     *
     * @param jsonArray 仓库信息
     * @return 仓库ID
     * @throws XbbException 异常
     */
    private Long getWarehouseId(JSONArray jsonArray) throws XbbException {
        if (CollectionsUtil.isEmpty(jsonArray)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.WAREHOUSE_INFO_NOT_EMPTY);
        }
        JSONObject warehouseInfo = jsonArray.getJSONObject(0);
        if (MapUtils.isEmpty(warehouseInfo)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.WAREHOUSE_INFO_NOT_EMPTY);
        }
        String warehouseName = warehouseInfo.getString(BusinessConstant.NAME);
        Long warehouseId = warehouseInfo.getLong(BusinessConstant.ID);
        if (Objects.isNull(warehouseId)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100002, MessageConstant.WAREHOUSE_INFO_NOT_EMPTY, warehouseName);
        }
        return warehouseId;
    }

    private Long getGuaranteePeriod(Long guaranteePeriod) {
        if (Objects.isNull(guaranteePeriod)) {
            return 0L;
        }
        return guaranteePeriod * 86400;
    }

}
